פרצות אבטחה נפוצות בהעלאת קבצים בעזרת PHP מאת Hyp3rInj3cT10n העלאת קבצים ושיתופם הוא עניין שהפך לנפוץ מאוד בימינו: לא פעם ולא פעמיים אנחנו נתקלים במצבים שבהם אנו צריכים ו/או רוצים לשתף קבצים - בין אם מדובר בתמונות, סירטונים או קבצים מסוגים אחרים שנועדו בין השאר לצפיה או להורדה. מפתחי ה- PHP יצרו עבור בונה האתר מספר פונקציות כדוגמת הפונקציה,move_uploaded_file שמעלה קבצים לשרת. הפונקציה עושה את עבודתה בצורה יפה מאוד, אך משאירה את האחריות לבדוק את הקובץ בידי המתכנת עצמו. נתמקד במאמר זה בפרצות נפוצות ואפשריות שנוצרות בעת תיכנות מערכות העלאת קבצים. לשם הנוחות, נשתמש בדוגמאות בשפת PHP אחרות. אך רב הדוגמאות שאביא מקבילות גם בשפות צד שרת אפשרויות בקובץ ההגדרות של ה- PHP לפני שנתחיל להתעסק בסקריפטים ב- PHP, אנחנו צריכים להכיר ולטפל קודם כל בסביבת העבודה שלנו: השרת. ישנן מספר הגדרות שנרצה לערוך ולוודא. ההגדרה הראשונה: בקובץ ההגדרות של PHP קיימת ההגדרה,file_uploads שקובעת האם יהיה ניתן להעלות קבצים באמצעות פרוטוקול ה- HTTP. אנחנו כמובן נוודא שאפשר להעלות קבצים באמצעות ה- HTTP, נוודא שההגדרה אכן דלוקה, כלומר: file_uploads = On upload_max_filesize קובעת את הגודל המירבי המותר לקובץ המועלה באמצעות ה- HTTP. עם זאת, אל תבנו על ההגדרה הזו לצורך אבטחת השרת שלכם - תמיד עדיף לבצע בדיקה ידנית שאותה נציג. לצורך הדוגמא נגדיר: upload_max_filesize = 10M מאחר ואנו משתמשים במתודת POST לצורך עניין העלאת קבצים יש עוד מספר דברים להגדיר. ההגדרה post_max_size מייצגת את הגודל המירבי של בקשה המשתמשת בשיטה.POST הגדרה זו משפיעה גם על העלאות קבצים באמצעות השיטה,POST ולכן יהיה כדאי להשאיר את הערך של ההגדרה הזו בדוגמא
לערך של ההגדרה upload_max_filesize או ערך הגדול ממנו כדי למנוע בעיות עם גודלו המירבי של הקובץ. בדוגמאות במסמך זה נשתמש בהגדרה הבאה: post_max_filesize = 11M max_input_time מגדירה את הזמן )בשניות( שמותר ל- PHP לחכות כדי לקבל את המידע. בעת העלאת קבצים גדולים בחיבורי אינטרנט איטיים, הזמן עלול לעבור את הערך,max_input_time ואז ההעלאה תפסק ותכשל. אפשר להגדיר את max_input_time כמספר גדול כדי למנוע מצב כזה: max_input_time = 9999 עם זאת, עדיף להשאיר הגדרה זו בערך מעשי, למשל 03 או 03 כדי למנוע ניצול משאבים גרוע. max_input_time = 60 MAX_FILE_SIZE מגדיר את גודל הקובץ המקסימלי, האומנם? להלן דוגמא שכיחה לקוד לבדיקת גודל הקובץ: <?php if ( isset($_files['file']) ) $name = $_FILES['file']['name']; $tmp = $_FILES['file']['tmp_name']; $error = $_FILES['file']['error']; if ( $error == UPLOAD_ERR_OK && move_uploaded_file($tmp, $name) ) echo 'Your file has been uploaded.'; echo 'An error has been occurred, file not uploaded.'; echo '<br /><br />'; echo <<<END File: <input type="file" name="file" /> <input type="hidden" name="max_file_size" value="100000" /> <input type="submit" value="upload" /> </form> END;?> 0
הגדרנו בעזרת MAX_FILE_SIZE את גודל הקובץ המקסימלי ל- 033 אלף בייטים. אם גודל הקובץ יעלה על הגודל שצויין תוחזר שגיאה והתהליך יפסק. בפתרון זה יש בעיה גדולה: MAX_FILE_SIZE מוגדר בצד הלקוח. כלומר, הוא ניתן לשינוי. אפשר לשנות את MAX_FILE_SIZE באמצעות Injection, Javascript Form Manipulation ודרכים רבות נוספות. דוגמא ל- Manipulation Form להלן הטופס שלנו: File: <input type="file" name="file" /> <input type="hidden" name="max_file_size" value="100000" /> <input type="submit" value="upload" /> </form> כפי שניתן לראות בבירור, MAX_FILE_SIZE מוגדר בטופס שלנו כ- 033,333 בתוך שדה input מוסתר Firefox האפשרות הפשוטה ביותר היא להשתמש באחת מתוך אלפי התוספות של הדפדפן.)hidden( בשביל לשנות את סוג ה- input שנבחר, בהתאם למה שנרצה להעלות. ממוסתר לטקסט,)text( כדי שנוכל לשנות את ה- 033,333 לכל ערך אפשרות שניה שחשוב להכירה היא השיטה הידנית - שיכתוב הקוד ללא כלים: נעתיק את הקוד ונשנה את שדה ה- hidden ל- text, )נוכל גם לשנות את ערך ברירת המחדל אם נרצה(. בסיום העריכה נשמור את הקובץ כ- HTML. הקוד שלנו צריך להראות כך: File: <input type="file" name="file" /> <input type="text" name="max_file_size" value="5000000" /> <input type="submit" value="upload" /> </form> כדי שהדוגמא תעבוד יש לעדכן את כתובת הטופס )דבר שאנשים נוטים לשכוח(. נחזור שוב לשורה הראשונה: הסימן "?" )סימן שאלה( מייצג למעשה את שורת הכתובת, והמשתנים שבאים לאחריה. זוהי דרך קצרה מאוד להפנות את הקובץ לעצמו, במקום להשתמש במשתנים ולהסתבך בחיפוש שם וכתובת הקובץ. כדי שהטופס יעבוד נשנה את הערך של ה- action לכתובת של הקובץ המעלה את הקבצים, לדוגמא: <form action="http://example.com/upload.php" method="post" enctype="multipart/form-data"> 3
שימו לב שבמידה ויש מידע המקשר אל עמוד ההעלאה והוא מועבר באמצעות שורת הכתובת יש לצרף אותו. לדוגמא: <form action="http://example.com/index.php?page=upload&terms=agreed" method="post" enctype="multipart/form-data"> דוגמא ל- Injection Javascript דרך נוספת היא שימוש בקוד Javascript כדי לשנות את הערך של.MAX_FILE_SIZE שיטה זו נקראית לעתים גם.Javascript Manipulation אנחנו משתמשים בכך שניתן לכתוב פקודות JavaScript בשורת הכתובת של הדפדפן, שיפעלו על החלון הנוכחי. נסו למשל לכתוב בדפדפן שלכם בשורת הכתובת את השורה: javascript:alert('can you see this?'); הריצו את הקוד בדפדפן שלכם, בשורת הכתובת. תקפוץ לכם הודעת Alert עם הטקסט. כעת נראה את אופן הניצול האפשרי בדוגמא שהצגנו בפרק הקודם. הפקודה הבאה תראה את הערך של :MAX_FILE_SIZE javascript:alert(document.getelementsbyname("max_file_size")[0].value); הפקודה הבאה תשנה ערך זה: javascript:document.getelementsbyname("max_file_size")[0].value=999999;return; השתמשנו ב- getelementsbyname, ובחרנו ב- 3 את ערכו של MAX_FILE_SIZE כ- 999999. צורת כתיבה נוספת: שמייצג את המופע הראשון )והיחיד(. הגדרנו מחדש javascript:void(document.getelementsbyname("max_file_size")[0].value=999944); אם לא נשתמש ב- void )או ב- return ( אנחנו נועבר לדף שאינו קיים או לדף לבן - הדפדפן רוצה להציג לנו את הפלט של הקוד שלנו. לקוד שאנחנו רוצים לבצע לא אמור להיות פלט מיוחד ואנחנו רוצים להשאר באותו הדף, ולשם כך בדיוק אנו משתמשים ב- void. פונקציה זו לא מחזירה פלט, ובעזרתה אנחנו לא נועבר לדף לא רצוי. לפני שנמשיך, נראה יתרון משמעותי נוסף שנותנת לנו הפונקציה,alert בעזרת הדוגמא הבאה: javascript:alert(document.getelementsbyname("max_file_size")[0].value); 4
לא עשינו שום דבר מיוחד - הכנסנו את הערך של MAX_FILE_SIZE יכולות להיות 2 תגובות אפשריות: לפונקצית ה- Alert. לפעולה הזאת תקפוץ הודעת Alert עם התוכן של.MAX_FILE_SIZE נוכל לראות שבאמת שינינו את ערכו. לא יקרה כלום. נסיק כי טעינו והפניה לא נכונה ו/או שהערך המוחזר הוא ריק. )יש דפדפנים שכן יציגו Alert ריק(.0.2 נמשיך לעוד דוגמא שבעזרתה נוכל לערוך את הערך ב- MAX_FILE_SIZE : javascript:void(document.getelementsbyname("max_file_size")[0].type='text'); שורה זו משנה את הסוג של ה- input מ- hidden לטקסט. כעת, ניתן לשנות את הערך שלו בטופס עצמו. התגוננות 5 כדי להתגונן נפעיל בדיקה נוספת בצד השרת לבדיקת הערך של :MAX_FILE_SIZE <?php $maxfilesize = 100000; if ( isset($_files['file']) ) $name = $_FILES['file']['name']; $tmp = $_FILES['file']['tmp_name']; $error = $_FILES['file']['error']; if ( isset($_post['max_file_size']) && $maxfilesize == $_POST['MAX_FILE_SIZE'] ) if ( $error == UPLOAD_ERR_OK && move_uploaded_file($tmp, $name) ) echo 'Your file has been uploaded.'; echo 'An error has been occurred, file not uploaded.'; echo 'Were you trying to trick us?'; echo '<br /><br />'; echo <<<END File: <input type="file" name="file" /> <input type="hidden" name="max_file_size" value="$maxfilesize" /> <input type="submit" value="upload" /> </form>
END;?> נוכל גם לכתוב פתרון שלא יתבסס כלל על :MAX_FILE_SIZE <?php if ( isset($_files['file']) ) $name = $_FILES['file']['name']; $tmp = $_FILES['file']['tmp_name']; $error = $_FILES['file']['error']; $size = $_FILES['file']['size']; if ( $size < 100000 ) if ( $error == UPLOAD_ERR_OK && move_uploaded_file($tmp, $name) ) echo 'Your file has been uploaded.'; echo 'An error has been occurred, file not uploaded.'; echo 'Were you trying to trick us?'; echo '<br /><br />'; echo <<<END File: <input type="file" name="file" /> <input type="submit" value="upload" /> </form> END;?> החלפת קובץ שמועלה בקובץ קיים מה יקרה אם נרצה להעלות תמונה בשם bg.jpg בזמן שהיא כבר קיימת על השרת? התמונה החדשה שנעלה תחליף את הישנה. מה יקרה אם תוקף יעלה קובץ שתוכנו "Hacked" בשם זהה לזה של קובץ האינדקס של המערכת? יכולים להווצר מצבים בהם קבצים חשובים באתר מוחלפים בקבצים אחרים! הגדרת מיקום הקובץ מומלץ מאוד להגדיר תיקיה נפרדת לקבצים שמועלים לשרת, כדי למנוע מצבים בהם מוחלף קובץ קיים באחר. השורה בדוגמא שמגדירה את האופן שבו ישמר הקובץ היא: כדי להכניס את הקובץ לתיקיה, פשוט נוסיף את שמה: $name = $_FILES['file']['name']; $name = 'uploads/'.$_files['file']['name']; 6
כעת, הקבצים יועלו לתיקיה.uploads הגדרת שם הקובץ הצלחנו לגרום לקבצים לא להחליף את הקבצים שבתיקייה שלנו בעזרת הפרדה שלהם לתיקיה אחרת, אך עדיין לא פתרנו את כל הבעיות. לדוגמא, מה יקרה אם נעלה קובץ בשם program.exe שסוגר פירצות אבטחה במחשב, ולאחר מכן גולש אחר יעלה וירוס קטלני בשם?program.exe הקובץ החדש יחליף את הקובץ הקיים, ויהיה קשה לנו להבחין בכך. הפתרון שלנו יהיה כמובן בעזרת שינוי שמות הקבצים. נגדיר שמות חדשים לקבצים שמועלים: ניצור לקבצים שמועלים על ידי הגולשים שמות חדשים משלנו. נדאג ששמות שניצור לקבצים שמועלים לא יוכלו לחזור על עצמו. לדוגמא, פתרון אפשרי יכול להיות: $name = 'uploads/'.time().crc32($_files['file']['name'].time()).'- '.$_FILES['file']['name']; הוספנו לשם הקובץ מקדם שסביר להניח שלא יוכל לחזור על עצמו, מאחר והוא מתבסס על שני מרכיבים דינאמיים מאוד: שם הקובץ והזמן הנוכחי. ההסתברות ששני קבצים עם שם זהה יועלו במקביל היא אפסית. סיומות קבצים הגנה נוספת היא להגביל את סוג הקבצים שניתן להעלות לשרת )סוג הקבצים - על פי הסיומת שלהם(. בדרך כלל אנשים נוטים להגדיר סיומות אסורות Filter(,)Black List ולא אילו סיומות מותרות ( List White.)Filter למרות שזו גישה מאוד אינטואיטיבית, היא אינה נכונה ברוב המקרים - נראה זאת כעת. להלן דוגמת קוד לבדיקת סיומות אסורות: <?php $maxfilesize = 100000; $exts = array('php','cgi','html'); if ( isset($_files['file']) ) $name = 'uploads/'.time().crc32($_files['file']['name'].time()).'- '.$_FILES['file']['name']; $tmp = $_FILES['file']['tmp_name']; $error = $_FILES['file']['error']; if ( isset($_post['max_file_size']) && $maxfilesize == $_POST['MAX_FILE_SIZE'] ) $extension = pathinfo($tmp); $extension = strtolower($extension['extension']); 7
if ( in_array($ext,$exts) ) echo 'Bad extension'; if ( $error == UPLOAD_ERR_OK && move_uploaded_file($tmp, $name) ) echo 'Your file has been uploaded.'; echo 'An error has been occurred, file not uploaded.'; echo 'Were you trying to trick us?'; echo '<br /><br />'; $extstext = implode(', ',$exts); echo <<<END File: <input type="file" name="file" /> <input type="hidden" name="max_file_size" value=" $maxfilesize" /> <input type="submit" value="upload" /><br /> Disabled Extensions: $extstext. </form> END;?> אנחנו מקבלים רשימה של סיומות קבצים אסורות. אם המערכת מזהה סיומת מסוכנת, תהליך ההעלאת הקובץ מבוטל. נעבור כעת על מספר דוגמאות לדרכי ניצול פוטנציאליות במערכות הבנויות באופן זה או דומה. הרצת קוד בצד השרת )למשל )PHP בדוגמא אמנם הסיומת php חסומה, אך עדיין עומדות לרשותינו עוד הרבה סיומות שגם הן מריצות קודים ב- PHP, למשל:.php3, php4, php5, php6, phtml יש לא מעט אנשים שמעולם לא נתקלו בסיומות האלה ולכן לא יחסמו אותן כאשר הם ישבו לכתוב את הפילטר שהצגנו קודם לכן. בנוסף שימו לב שיש אפשרות לנסות להריץ קבצי perl ו- shtml אם השרת תומך בכך. 8
הרצת קודים עם שפות צד לקוח הסיומת html אומנם חסומה לנו, אך בכל זאת נוכל לנסות להריץ קודים ב- HTML על ידי שימוש בסיומת Javascript, Flash, Java גם לנסות להפעיל ולהריץ נוכל מאחר ואנו יכולים להריץ קודים ב- HTML,.htm VBScript ועוד... ו- htpasswd htaccess קבצי htaccess ו- htpasswd וענייני אבטחה הקשורים בהם מופיעים במאמר מאת אפיק בגליון זה. קבצים אלו נשמרים בשמות:.htpasswd,.htaccess שימו לב - שמם של הקבצים הוא בעצם סיומת בלבד! בעלי אתרים רבים מכירים את הסיומות האלה, אבל הם לא חושבים עליהם כשהם חוסמים סיומות קבצים. כדי לספק לכם המחשה לסכנה, בואו נראה מספר דוגמאות מסוכנות לפעולות הניתנות לביצוע בעזרת קובץ ה- htaccess : חסימת הכניסה לתיקיה: )כך אף אחד לא יכול להכנס לתיקיה בעזרת דפדפן האינטרנט( שינוי שם קובץ האינדקס לקובץ שלנו: )בהנחה שהעלנו קובץ הנקרא )deface.html deny from all DirectoryIndex deface.html הפניית הגולש מקובץ האינדקס לקובץ מרוחק: Redirect index.php http://my-site.com/deface.html AuthName "Please Identify" AuthType Basic AuthUserFile.htpasswd Require valid-user סגירת התיקיה בסיסמה: קוד זה יקפיץ חלון המבקש שם משתמש וסיסמה, כאשר הכותרת תהיה.Please Identify במקרה הזה נצטרך גם להשתמש ב- htpasswd. קובץ זה יכיל שמות משתמשים וסיסמאות, לדוגמא: username:encrypted-password username:encrypted-password username:encrypted-password 9
שם המשתמש מופרד מהסיסמה המוצפנת בעזרת התו : )נקודתיים(, וכל שורה מפרידה בין המשתמשים האחרים. על אופן הצפנת הסיסמה הרחבנו במאמר אחר בגליון זה. בנוסף יש מספיק כלים באינטרנט לטיפול בעניין, כמו: http://tools.dynamicdrive.com/password/ התגוננות נגדיר רשימה של סיומות מותרות, במקום רשימה של סיומות אסורות: <?php $maxfilesize = 100000; $exts = array('jpg','bmp','png','gif','txt','rar','doc','ppt'); //etc... if ( isset($_files['file']) ) $name = 'uploads/'.time().crc32($_files['file']['name'].time()).'- '.$_FILES['file']['name']; $tmp = $_FILES['file']['tmp_name']; $error = $_FILES['file']['error']; if ( isset($_post['max_file_size']) && $maxfilesize == $_POST['MAX_FILE_SIZE'] ) $extension = pathinfo($tmp); $extension = strtolower($extension['extension']); if (!in_array($ext,$exts) ) echo 'Bad extension'; if ( $error == UPLOAD_ERR_OK && move_uploaded_file($tmp, $name) ) echo 'Your file has been uploaded.'; echo 'An error has been occurred, file not uploaded.'; echo 'Were you trying to trick us?'; echo '<br /><br />'; $extstext = implode(', ',$exts); echo <<<END File: <input type="file" name="file" /> <input type="hidden" name="max_file_size" value=" $maxfilesize" /> <input type="submit" value="upload" /><br /> Allowed Extensions: $extstext. </form> END;?> 02
Null Byte Poisoning ה- Byte Null הוא התו הראשון בטבלת ה- ASCII, ובדרך כלל המערכת נעזרת בו בכדי לזיהות מחרוזות. ב- HEX התו מיוצג כ- 00 )או %00( וב- PHP : ה- Byte Null הוא תו בלתי נראה. לא ניתן לראות אותו. chr(0) )Null Byte גם כ- Attack )מוכר Null Byte Poisoning לצורך ההסבר, נגיד שהתו & )אמפטסנד( יהיה ה- Byte,Null כדי שאוכל להעביר בצורה טובה יותר את ההדגמה. דמיינו לכם מערכת הכוללת פונקציה להעלאת קבצים כמו זו שנמצאת בדוגמת הקוד האחרונה עם הסיומות. כעת בואו נראה מה למשל יקרה אם שם הקובץ שנכניס יהיה השם הבא: image.jpg&.php בבדיקה שנעשה בעזרת ה- PHP, הביטוי שיבדק יהיה אך ורק: image.jpg והבדיקה תעבור בהצלחה. כאשר הקובץ יווצר, ה- Byte Null יצונזר ושם הקובץ יהיה: image.jpg.php התגוננות לכאורה נראה כי מדובר בשיטה שלא ניתנת לעצירה, אך זה לא נכון. כדי להתגונן יש להבריח או לצנזר את ה- Byte Null כך שלא יוכל לתפקד. נוכל לצנזר את ה- Byte Null בעזרת השורה הבאה: $name = str_replace(chr(0),'',$_files['file']['name']); ה- Quotes Magic מבצעים הברחה אוטומטית ל- Byte,Null אך כמובן שלא באמת נסמוך עליהם. Local File Disclosure בכל הדוגמאות עד כה שהצגנו, הצגנו הודעה אם הקובץ הועלה או שהתרחשה שגיאה והוא לא הועלה. לדוגמאות זה היה נחמד מאוד, אבל כשאנחנו מדברים על מערכת אמיתית, אנחנו נספק למשתמש קישור להורדת הקובץ. אין בעיה לתת קישור כזה - הרי יש לנו את המשתנה name שמכיל את שם הקובץ, ונוכל להשתמש בו כהפניה. צריך ליצור קובץ שיודיע לדפדפן להוריד את הקובץ, לדוגמא: <?php if ( isset($_get['file']) && is_string($_get['file']) ) if ( @file_exists($_get['file']) && @is_readable($_get['file']) ) header("content-type: application/octet-stream"); 00
header('content-disposition: attachment; filename="'.$_get['file'].'"'); readfile('uploads/'.$_get['file']);?> שורת הכתובת צריכה להיות כזאת: )כאשר filename מייצג את שם הקובץ שנרצה להוריד(?file=[filename] Path Traversal השיטה Path Traversal )מוכרת גם כ- Traversal )Directory משמשת אותנו ל"חציית" התיקיה בה אנו נמצאים. יש 2 סימונים מוכרים שיש להכיר:. )נקודה אחת( - מייצגת את התיקיה הנוכחית.. )שתי נקודות( - מייצגות תיקייה אחת למעלה. Directory( )Parent מה אם נשתמש ב- Traversal Path כדי לעלות תיקיה למעלה??file=../index.php מה שיקרה זה: readfile('uploads/../index.php'); כלומר, ניגש לתיקיה uploads ואז לתיקיה מעליה )חזרנו אחורה(, ומשם נקרא את הקובץ שנקרא.index.php המשמעות היא שכעת נוכל להוריד ולקרוא כל קובץ שנרצה מהאתר. בתוך קובץ האינדקס נוכל למצוא בדרך כלל פקודות ייבוא לקבצי תצורה אחרים, שגם אותם נוכל להוריד למחשב ולקרוא. לדוגמא, אפשר למצוא בדרך כלל בקבצי האינדקס של המערכת שמות/מיקום של קבצי הקונפיגורציה הכוללים מידע שימושי רב, כמו למשל את פרטי ההתחברות למסד הנתונים ( Connection )String המשמש את המערכת באיכסון המידע, קריאות לקבצי קונפיגורציה התומכים במערכת וכו'. אם זה לא מספיק, תמיד אפשר לחפש אחר קבצי htaccess, htpasswd ועוד קבצים שאין גישה אליהם דרך המערכת. 00
התגוננות ההתגוננות היא פשוטה מאוד: נצנזר את התווים.. )שתי נקודות( ו-/ )סלאש(. לדוגמא: $file = $_GET['file']; $file = str_replace('/','',$file); $file = str_replace('..','',$file); יש צורך להחליף אף את התווים הנ"ל בסוגי הקידודים השונים הנתמכים ע"י טכנולוגיית המערכת. File Download Injection <?php if ( isset($_get['file']) && is_string($_get['file']) ) $file = $_GET['file']; $file = str_replace(chr(0),'',$file); $file = str_replace('..','',$file); header("content-type: application/octet-stream"); header('content-disposition: attachment; filename="'.$file.'"'); readfile('uploads/'.$file);?> HTTP/1.1 200 OK Date:... Content-Type: application/octet-stream Content-Disposition: attachment; filename=[file name] Content-Length: [file length] [file content] להלן הדוגמא שבה נשתמש: התגובה שנקבל מהשרת כשניגש לקובץ תהיה בערך כזאת: כשנגש לקובץ בצורה הבאה:?file=roy.bat%0a%0dContent-Length%3A%2016%0a%0d%0a%0dshutdown%20-s%20-t%2060 CRLF = a%0d )ירידת שורה( = A%0 : )נקודתיים( = %23 רווח כלומר: roy.bat Content-Length: 16 03
shutdown -s -t 60 נקבל: HTTP/1.1 200 OK Date: [...] Content-Type: application/octet-stream Content-Disposition: attachment; filename=roy.bat Content-Length: 16 shutdown -s -t 60 Content-Length: [...] הדפדפן יתייחס לכותר ה- Content-Length הראשון שישלח לו מהשרת, ולכן יקרא רק את 00 הראשונים. למעשה, הדפדפן יוריד קובץ שנקרא roy.bat ותוכנו: התווים shutdown -s -t 60 הקובץ הוא קובץ אצווה וברגע שהגולש יפתח את אותו, תופיע לו הודעה שמחשבו ייכבה בעוד 03 שניות. אתם מוזמנים להפעיל את זה כדי לבדוק. לביטול הפעולה יש להריץ בשורת ההפעלה או ב- cmd את זה: shutdown -a במקרה הזה השתמשתי דווא בקובץ אצווה,)batch( אך כמובן שנוכל להשתמש במגוון רחב של קבצים. התגוננות נוודא כי הקובץ אכן קיים וניתן לקריאה לפני שניתן אותו לגולש. לדוגמא: <?php if ( isset($_get['file']) && is_string($_get['file']) ) $file = $_GET['file']; $file = str_replace('/','',$file); $file = str_replace('..','',$file);?> if ( @is_readable($_get['file']) ) header("content-type: application/octet-stream"); header('content-disposition: attachment; filename="'.$file.'"'); readfile('uploads/'.$file); 04
עקיפת מכסת הקבצים שניתן להעלות במקביל )לביצוע )DoS לפעמים נרצה לאפשר העלאה של יותר מקובץ אחד במקביל, למשל 5 קבצים. כך יראה הטופס: File #1: <input type="file" name="file1" /><br /> File #2: <input type="file" name="file2" /><br /> File #3: <input type="file" name="file3" /><br /> File #4: <input type="file" name="file4" /><br /> File #5: <input type="file" name="file5" /><br />... לשם ביצוע ההתקפה יש לנו מספר אפשרויות: בניית פונקציה שמתעסקת בהעלאת הקובץ, ונזמן אותה 5 פעמים. העתקת הקוד והדבקתו עוד 4 פעמים, כשבכל פעם נשנה את שם המשתנה שאיתו עובדים. להשתמש בלולאת.foreach הפיתרון השלישי הוא הנפוץ ביותר כיום והוא גם הפתרון הנוח ביותר לשימוש, אך הוא גם הפתרון הלוקה.0.2.0 בחסר. 05 דוגמא לקוד המבצע את הפיתרון השלישי: foreach ( $_FILES as $file ) $name = 'uploads/'.time().crc32($file['name'].time()).'-'.$file['name']; $tmp = $file['tmp_name']; $error = $file['error']; if ( isset($_post['max_file_size']) && $maxfilesize == $_POST['MAX_FILE_SIZE'] ) $extension = pathinfo($tmp); $extension = strtolower($extension['extension']); if (!in_array($ext,$exts) ) echo 'Bad extension'; if ( $error == UPLOAD_ERR_OK && move_uploaded_file($tmp, $name) ) echo 'Your file has been uploaded.'; echo 'An error has been occurred, file not uploaded.'; echo 'Were you trying to trick us?'; echo '<br /><br />'; הפיתרונות הראשון והשני הם סטאטים, ומיועדים להעלות רק עד חמישה קבצים בכל מילוי טופס שמתבצע, ולא יותר. לעומתם, הפיתרון השלישי יכול להעלות כמות משתנה של קבצים- כמספר הקבצים שהוא מקבל. לכן, אופן הניצול יהיה - Form Manipulation נערוך את הטופס ונשמור בקובץ חדש: )לא לשכוח את ה- action!(
<form action="[file-url]" method="post" enctype="multipart/form-data"> File #1: <input type="file" name="file1" /><br /> File #2: <input type="file" name="file2" /><br /> File #3: <input type="file" name="file3" /><br /> File #4: <input type="file" name="file4" /><br /> File #5: <input type="file" name="file5" /><br /> File #6: <input type="file" name="file6" /><br /> File #7: <input type="file" name="file7" /><br /> File #8: <input type="file" name="file8" /><br /> File #9: <input type="file" name="file9" /><br /> File #10: <input type="file" name="file10" /><br />... התגוננות לפתירת הבעיה קיימות מספר אפשרויות, אך מספיק להציג שתיים הן: שבירת הלולאה וביטול הלולאה. foreach ($_FILES as $key => $array) if ( $key == 4 ) // 0,1,2,3,4 = 5 break; if ( count($_files) <= 5 ) foreach ($_FILES as $array) דוגמא ראשונה - שבירת הלולאה: דוגמא שניה - ביטול הלולאה: סיכום במאמר זה נגעתי במספר נרחב של נקודות המופיעות בהרבה מנגנוני העלאת קבצים במערכות השונות. חשוב לזכור שבכל מערכת ומערכת יכולים להווצר סוגים שונים של חורים, אך אחד העקרונות החשובים ביותר כשמדובר בפיתוח במערכות המקבלות קלט )כל קלט שהוא( מהמשתמש הוא שלעולם אין לסמוך על המשתמש ותמיד יש לבצע בדיקות מקיפות ולוודא שאכן הקלט עומד בסטנדרטים שקבענו. 06